package services

import (
	"encoding/json"
	"github.com/pkg/errors"
	"go.didapinche.com/foundation/apisix-dashboard/internal/app/models"
	"go.didapinche.com/foundation/apisix-dashboard/internal/app/repository"
	"go.didapinche.com/foundation/apisix-dashboard/internal/pkg/utils"
	"go.etcd.io/etcd/clientv3"
	"go.uber.org/zap"
	"sort"
	"strings"
	"time"
)

type RouteService interface {
	Get(key, env string) (*models.Route, error)
	List(name, env string, pageSize, pageNum int) (*models.RoutePage, error)
	Create(upstream *models.Route, env string) error
	Update(upstream *models.Route, env string) error
	Delete(key, env string) error
	GetName(name string, env string) (bool, error)
	Syn(route *models.Route, env []string) error
}

type routeService struct {
	etcd            repository.EtcdRepository
	client          map[string]*clientv3.Client
	log             *zap.Logger
	service         Service
	upstreamService UpstreamService
}

func NewRouteService(
	etcd repository.EtcdRepository,
	service Service,
	upstreamService UpstreamService,
	log *zap.Logger,
	client map[string]*clientv3.Client,
) RouteService {
	return &routeService{
		etcd:            etcd,
		log:             log,
		client:          client,
		upstreamService: upstreamService,
		service:         service,
	}
}

func (s routeService) Get(key, env string) (*models.Route, error) {
	param, err := s.etcd.GetResp(s.client[env], "/apisix/routes/"+key)
	if err != nil {
		return nil, errors.Wrap(err, "call etcd.Get failed")
	}
	u := new(models.Route)
	err = json.Unmarshal([]byte(param.Kvs[0].Value), &u)
	if u.ID == "" {
		id := string(param.Kvs[0].Key)
		u.ID = strings.ReplaceAll(id, "/apisix/routes/", "")
	}
	if u.Name == "" {
		u.Name = u.ID
	}
	if len(u.Uris) < 1 {
		u.Uris = append(u.Uris, u.URI)
	}
	if err != nil {
		return nil, errors.Wrap(err, "call json.Unmarshal failed")
	}
	return u, nil
}

func (s routeService) List(name, env string, pageSize, pageNum int) (*models.RoutePage, error) {
	params, err := s.etcd.ListResp(s.client[env], "/apisix/routes/")
	if err != nil {
		return nil, errors.Wrap(err, "call etcd.Get failed")
	}
	us := make([]*models.Route, 0)
	for i := 0; i < len(params.Kvs); i++ {
		u := new(models.Route)
		err = json.Unmarshal([]byte(params.Kvs[i].Value), &u)
		if u.ID == "" {
			id := string(params.Kvs[i].Key)
			u.ID = strings.ReplaceAll(id, "/apisix/routes/", "")
		}
		if u.Name == "" {
			u.Name = u.ID
		}
		if len(u.Uris) < 1 {
			u.Uris = append(u.Uris, u.URI)
		}
		if err != nil {
			s.log.Error("路由解析失败")
		} else {
			if name != "" { //搜索根据名字和uris匹配
				if strings.Contains(u.Name, name) {
					us = append(us, u)
				} else {
					for _, r := range u.Uris {
						if strings.Contains(r, name) {
							us = append(us, u)
						}
					}
				}
			}
			if name == "" {
				us = append(us, u)
			}
		}
	}

	sort.Sort(models.RouteSlice(us))
	routes := new(models.RoutePage)
	if len(us) < pageSize {
		routes.Routes = us
	} else {
		routes.Routes = us[(pageNum-1)*pageSize : pageNum*pageSize]
	}
	routes.Total = len(us)
	return routes, nil
}

func (s routeService) Create(route *models.Route, env string) error {
	if route.ID == "" {
		route.ID = utils.GetFlakeUidStr()
	}
	route.CreateTime = time.Now().Unix()
	route.UpdateTime = time.Now().Unix()
	str, err := json.Marshal(route)
	if err != nil {
		return errors.Wrap(err, "call json.Marshal failed")
	}
	if err := s.etcd.Create(s.client[env], "/apisix/routes/"+route.ID, string(str)); err != nil {
		return errors.Wrap(err, "call etcd.Create failed")
	}
	return nil
}

func (s routeService) Update(route *models.Route, env string) error {
	route.CreateTime = time.Now().Unix()
	route.UpdateTime = time.Now().Unix()
	str, err := json.Marshal(route)
	if err != nil {
		return errors.Wrap(err, "call json.Marshal failed")
	}
	if err := s.etcd.Update(s.client[env], "/apisix/routes/"+route.ID, string(str)); err != nil {
		return errors.Wrap(err, "call etcd.Update failed")
	}
	return nil
}

func (s routeService) Delete(key, env string) error {
	if err := s.etcd.Delete(s.client[env], "/apisix/routes/"+key); err != nil {
		return errors.Wrap(err, "call etcd.Delete failed")
	}
	return nil
}

func (s routeService) GetName(name, env string) (bool, error) {
	params, err := s.etcd.List(s.client[env], "/apisix/routes/")
	if err != nil {
		return true, errors.Wrap(err, "call etcd.Get failed")
	}
	for i := range params {
		u := new(models.Upstream)
		err = json.Unmarshal([]byte(params[i]), &u)
		if err != nil {
			s.log.Error("路由名字解析失败")
		} else {
			if name == u.Name {
				return true, nil
			}
		}
	}
	return false, nil
}

func (s routeService) Syn(route *models.Route, env []string) error {
	for _, e := range env {
		if route.UpstreamID != "" {
			_, err := s.upstreamService.Get(route.UpstreamID, e)
			if err != nil {
				return errors.New("UpstreamID don't exists on " + e)
			}
		}
		if route.ServiceID != "" {
			_, err := s.service.Get(route.UpstreamID, e)
			if err != nil {
				return errors.New("ServiceID don't exists on " + e)
			}
		}
	}

	for _, e := range env {
		if err := s.Create(route, e); err != nil {
			return errors.Wrap(err, "call routeService.create error")
		}
	}
	return nil
}
